package com.example.customlayoutdemo;

import ohos.agp.components.AttrSet;
import ohos.agp.components.Component;
import ohos.agp.components.ComponentContainer;
import ohos.app.Context;

import java.util.HashMap;
import java.util.Map;

public class CustomLayout extends ComponentContainer implements Component.EstimateSizeListener, ComponentContainer.ArrangeListener {
    // 自定义构造方法
    public CustomLayout(Context context) {
        this(context, null);
    }
    // 要支持XML布局，需要实现此方法
    public CustomLayout(Context context, AttrSet attrSet) {
        super(context, attrSet);
        // 设置测算监听
        setEstimateSizeListener(this);
        // 设置布局监听
        setArrangeListener(this);
    }

    @Override
    public boolean onEstimateSize(int widthEstimatedConfig, int heightEstimatedConfig) {
        // 每次尺寸变化时，先清除缓存的布局信息数据
        clearValues();
        // 通知容器内的子组件进行自身的尺寸测量
        estimateChildSize(widthEstimatedConfig, heightEstimatedConfig);
        // 对子组件进行布局
        layoutChild(widthEstimatedConfig, heightEstimatedConfig);
        // 测量自身的尺寸
        estimateSelf(widthEstimatedConfig, heightEstimatedConfig);
        return true;
    }

    @Override
    public boolean onArrange(int i, int i1, int i2, int i3) {
        // 对各个子组件进行布局
        for (int idx = 0; idx < getChildCount(); idx++) {
            Component childView = getComponentAt(idx);
            Layout layout = layoutMap.get(idx);
            if (layout != null) {
                childView.arrange(layout.x, layout.y, layout.width, layout.height);
            }
        }
        return true;
    }

    // 记录当前布局到的x轴位置
    private int currentX = 0;
    // 记录当前布局到的y轴位置
    private int currentY = 0;
    // 记录当前布局容器最大宽度
    private int maxWidth = 0;
    // 记录当前布局容器最大高度
    private int maxHeight = 0;
    // 记录布局容器中上一行的高度
    private int lastHeight = 0;

    // 子组件的布局结构模型
    private class Layout {
        int x = 0; // x坐标
        int y = 0; // y坐标
        int width = 0; // 宽度
        int height = 0; // 高度
    }
    // 存储所有子组件的布局数据，key存储子组件的index，value存储Layout对象
    private Map<Integer, Layout> layoutMap = new HashMap<Integer, Layout>();

    private void clearValues() {
        currentX = 0;
        currentY = 0;
        maxWidth = 0;
        maxHeight = 0;
        layoutMap.clear();
    }

    private void estimateChildSize(int widthEstimatedConfig, int heightEstimatedConfig) {
        // 遍历所有容器内的子组件
        for (int  i = 0; i < getChildCount(); i++) {
            // 根据索引获取子组件实例
            Component component = getComponentAt(i);
            // 保护性判断
            if (component == null) {
                continue;
            }
            // 获取子组件所设置的layoutConfig
            LayoutConfig config = component.getLayoutConfig();
            // 子组件的宽高测算结果
            int widthSpec;
            int heightSpec;
            // 进行宽度测算
            if (config.width == LayoutConfig.MATCH_CONTENT) {
                // 子组件配置的是宽度适配返回对应测算结果
                widthSpec = EstimateSpec.getSizeWithMode(config.width, EstimateSpec.NOT_EXCEED);
            } else if (config.width == LayoutConfig.MATCH_PARENT) {
                // 子组件配置的是宽度充满父容器
                int parentWidth = EstimateSpec.getSize(widthEstimatedConfig);
                // 测算子组件的真实宽度
                int childWidth = parentWidth - component.getMarginLeft() - component.getMarginRight();
                // EstimateSpec.PRECISE为使用绝对数值
                widthSpec = EstimateSpec.getSizeWithMode(childWidth, EstimateSpec.PRECISE);
            } else {
                // 使用组件配置的宽度
                widthSpec = EstimateSpec.getSizeWithMode(config.width, EstimateSpec.PRECISE);
            }
            // 高度测算逻辑同宽度
            if (config.height == LayoutConfig.MATCH_CONTENT) {
                heightSpec = EstimateSpec.getSizeWithMode(config.height, EstimateSpec.NOT_EXCEED);
            } else if (config.height == LayoutConfig.MATCH_PARENT) {
                int parentHeight = EstimateSpec.getSize(heightEstimatedConfig);
                int childHeight = parentHeight - component.getMarginTop() - component.getMarginBottom();
                heightSpec = EstimateSpec.getSizeWithMode(childHeight, EstimateSpec.PRECISE);
            } else {
                heightSpec = EstimateSpec.getSizeWithMode(config.height, EstimateSpec.PRECISE);
            }
            // 调用子组件的测算方法
            component.estimateSize(widthSpec, heightSpec);
        }
    }

    private void layoutChild(int widthEstimatedConfig, int heightEstimatedConfig) {
        // 获取容器自身的布局宽度
        int layoutWidth = EstimateSpec.getSize(widthEstimatedConfig);
        // 遍历子组件
        for (int i = 0; i < getChildCount(); i++) {
            // 根据索引获取子组件
            Component component = getComponentAt(i);
            // 安全判断
            if (component == null) {
                continue;
            }
            // 创建子组件布局对象
            Layout layout = new Layout();
            // 计算x坐标，x坐标为当前布局到的x轴位置加上边距
            layout.x = currentX + component.getMarginLeft();
            // 计算y坐标，y坐标为当前布局到的y轴位置加上边距
            layout.y = currentY + component.getMarginTop();
            // 获取计算的组件宽度和高度
            layout.width = component.getEstimatedWidth();
            layout.height = component.getEstimatedHeight();
            if ((currentX + layout.width) > layoutWidth) {
                // 当组件的布局位置超出父容器时，换行
                // 换行后，x坐标从0开始
                currentX = 0;
                // y坐标自增上当前行的高度
                currentY += lastHeight;
                // 当前行的高度置为0
                lastHeight = 0;
                // 重设x坐标和和y坐标
                layout.x = currentX + component.getMarginLeft();
                layout.y = currentY + component.getMarginTop();
            }
            // 将计算完成的布局对象进行存储
            layoutMap.put(i, layout);
            // 矫正当前行的行高，行高取当前行中布局的最高的元素
            lastHeight = Math.max(lastHeight, layout.height + component.getMarginBottom());
            // 矫正当前布局到的x轴位置
            currentX += layout.width + component.getMarginRight();
            // 矫正当前的最大宽度和高度
            maxWidth = Math.max(maxWidth, layout.x + layout.width + component.getMarginRight());
            maxHeight = Math.max(maxHeight, layout.y + layout.height + component.getMarginBottom());
        }

    }

    private void estimateSelf(int widthEstimatedConfig, int heightEstimatedConfig) {
        // 获取宽度和高度的EstimateSpec模式
        int widthSpce = EstimateSpec.getMode(widthEstimatedConfig);
        int heightSpce = EstimateSpec.getMode(heightEstimatedConfig);
        // 进行宽度测算
        int widthConfig = 0;
        switch (widthSpce) {
            case EstimateSpec.UNCONSTRAINT:
            case EstimateSpec.PRECISE:
                // 使用绝对数值的宽度
                int width = EstimateSpec.getSize(widthEstimatedConfig);
                widthConfig = EstimateSpec.getSizeWithMode(width, EstimateSpec.PRECISE);
                break;
            case EstimateSpec.NOT_EXCEED:
                // 非绝对数值的模式，将宽度设置为子组件布局计算后的宽度最大值
                widthConfig = EstimateSpec.getSizeWithMode(maxWidth, EstimateSpec.PRECISE);
                break;
            default:
                break;
        }
        // 进行高度测算
        int heightConfig = 0;
        switch (heightSpce) {
            case EstimateSpec.UNCONSTRAINT:
            case EstimateSpec.PRECISE:
                // 使用绝对数值的高度
                int height = EstimateSpec.getSize(heightEstimatedConfig);
                heightConfig = EstimateSpec.getSizeWithMode(height, EstimateSpec.PRECISE);
                break;
            case EstimateSpec.NOT_EXCEED:
                // 非绝对数值的模式，将高度设置为配置的最大值
                heightConfig = EstimateSpec.getSizeWithMode(maxHeight, EstimateSpec.PRECISE);
                break;
            default:
                break;
        }
        setEstimatedSize(widthConfig, heightConfig);
    }
}
